package commons.lang;

import java.io.Serializable;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;

import commons.utils.ConversionUtils;
import commons.utils.StringUtils;

/**
 * 
 * <b>Class Money</b>
 * 
 * <p>
 * The Money class represents a United States monetary value, expressed in
 * dollars and cents. Internally, the value is represented using Java's
 * BigDecimal class.
 * </p>
 * 
 * <p>
 * Methods are provided to perform all the usual arithmetic manipulations
 * required when dealing with monetary data, including add, subtract, multiply,
 * and divide.
 * </p>
 * 
 * <p>
 * <b>Rounding</b>
 * </p>
 * <p>
 * Rounding does not occur during intermediate computations; maximum precision
 * (and accuracy) is thus preserved throughout all computations. Round-off to an
 * integral cent occurs only when the monetary value is externalized (when
 * formatted for display as a String or converted to a long integer). One of
 * several different rounding modes can be specified. The default rounding mode
 * is to discard any fractional cent and truncate the monetary value to 2
 * decimal places.
 * </p>
 * 
 * <p>
 * <b>Currency Format</b>
 * </p>
 * <p>
 * A Currency Format (an instance of DecimalFormat) is used to control
 * formatting of monetary values for display as well as the parsing of strings
 * which represent monetary values. By default, the Currency Format for the
 * current locale is used. For the United States, the default Currency Symbol is
 * the Dollar Sign ("$"). Negative amounts are enclosed in parentheses. A
 * Decimal Point (".") separates the dollars and cents, and a comma (",")
 * separates each group of 3 consecutive digits in the dollar amount.
 * </p>
 * <p>
 * Examples: $1,234.56 ($1,234.56)
 * </p>
 * 
 * <p>
 * <b>Immutability</b>
 * </p>
 * <p>
 * Money objects, like String objects, are <b>immutable</b>. An operation on a
 * Money object (such as add, subtract, etc.) does not alter the object in any
 * way. Rather, a new Money object is returned whose state reflects the result
 * of the operation. Thus, a statement like
 * </p>
 * <p>
 * <tt>money1.add(money2);</tt>
 * </p>
 * <p>
 * has no effect; it does not modify money1 in any way, and the result is
 * effectively discarded. If the intent is to modify money1, then you should
 * code
 * </p>
 * <p>
 * money1 = money1.add(money2);
 * </p>
 * <p>
 * which effectively replaces money1 with the result.
 * </p>
 * 
 * @see BigDecimal
 * 
 * @see DecimalFormat
 * 
 */
public class Money // Money Class
	extends Number implements Cloneable, // Money objects can be cloned
	Serializable, // Money objects are serializable
	Comparable<Money> {
    private static final long serialVersionUID = 1L;

    public static final Money ZERO = new Money(0);

    /**
     * The monetary value.
     */
    protected BigDecimal value = null; // The monetary value
    /**
     * The Rounding Mode. Specifies if and how the monetary value is to be
     * rounded off to an integral cent.
     */
    protected int roundingMode = BigDecimal.ROUND_HALF_UP; // Rounding Mode
    /**
     * The Currency Format, used for formatting and parsing a monetary value.
     * Refer to the Java API documentation for the DecimalFormat class for
     * information on formats.
     */
    protected DecimalFormat currencyFormat = (DecimalFormat) NumberFormat.getCurrencyInstance(); // Currency
    // format
    /**
     * The special monetary value of zero ($0.00).
     */
    protected static final BigDecimal BIGDECIMAL_ZERO = new BigDecimal("0").setScale(2); // The

    // value
    // $0.00

    /**
     * 
     * <b>Class InvalidScaleFactorException</b>
     * 
     * <p>
     * The InvalidScaleFactorException is thrown if an invalid scale factor is
     * specified (valid scale factors are 0, 1, and 2). This is a non-checked
     * exception and will be detected at runtime only.
     * </p>
     * 
     */
    public static class InvalidScaleFactorException extends RuntimeException {
	private static final long serialVersionUID = 1L;

	/**
	 * 
	 * Default constructor for a InvalidScaleFactorException object.
	 * 
	 */
	public InvalidScaleFactorException() {
	    super(); // Invoke super class constructor.
	}

	/**
	 * 
	 * Constructor for a InvalidScaleFactorException object.
	 * 
	 * <p>
	 * 
	 * @param info
	 *            Descriptive information
	 *            </p>
	 * 
	 */
	public InvalidScaleFactorException(String info) // Descriptive info
	{
	    super(info); // Invoke super class constructor.
	}
    } // Class InvalidScaleFactorException

    /**
     * 
     * <b>Class InvalidRoundingModeException</b>
     * 
     * <p>
     * The InvalidRoundingModeException is thrown if an invalid Rounding Mode is
     * specified (all rounding modes except ROUND_UNNECESSARY are valid). This
     * is a non-checked exception and will be detected at runtime only.
     * </p>
     * 
     * @see BigDecimal#ROUND_UP
     * @see BigDecimal#ROUND_DOWN
     * @see BigDecimal#ROUND_CEILING
     * @see BigDecimal#ROUND_FLOOR
     * @see BigDecimal#ROUND_HALF_UP
     * @see BigDecimal#ROUND_HALF_DOWN
     * @see BigDecimal#ROUND_HALF_EVEN
     * 
     */
    public static class InvalidRoundingModeException extends RuntimeException // Non-checked
    // exception
    {
	private static final long serialVersionUID = 1L;

	/**
	 * 
	 * Default constructor for a InvalidRoundingModeException object.
	 * 
	 */
	public InvalidRoundingModeException() {
	    super(); // Invoke super class constructor.
	} // Default Constructor InvalidRoundingModeException()

	/**
	 * 
	 * Constructor for a InvalidRoundingModeException object.
	 * 
	 * <p>
	 * 
	 * @param info
	 *            Descriptive information
	 *            </p>
	 * 
	 */
	public InvalidRoundingModeException(String info) // Descriptive info
	{
	    super(info); // Invoke super class constructor.
	}
    }

    /**
     * 
     * Default Constructor for a Money object; creates an object whose value is
     * $0.00.
     * 
     */
    public Money() {
	value = BIGDECIMAL_ZERO; // Initialize the monetary value
	// to $0.00.
    }

    public Money(Double value) {
	this(value.doubleValue());
    }

    /**
     * 
     * Constructs a Money object from a double-precision, floating-point value.
     * The Currency Format is set to the default format for the current locale.
     * 
     * <p>
     * The integral part of the value represents whole dollars, and the
     * fractional part of the value represents fractional dollars (cents). As an
     * example, the value 19.95 would represent $19.95.
     * </p>
     * 
     * <p>
     * 
     * @param <b>amount </b> The monetary amount, in dollars and cents
     *        </p>
     * 
     */
    public Money(double amount) // Monetary Value, dollars and cents
    {
	// In general, a double floating point value cannot represent a
	// decimal value exactly, and
	// therefore is only a very close approximation of the actual decimal
	// value. Fortunately,
	// the Double.toString() method is able to "recognize" an approximated
	// decimal value, and
	// will return the original (approximated) decimal value, rather than
	// the literal floating
	// point value. Here, we take advantage of this fact to obtain a
	// string representation of
	// the monetary value (without formatting, except for the decimal
	// point), which we then use
	// to create a BigDecimal object having the required value.
	// Were we not to make this simplifying assumption, the only
	// alternative would be to parse the
	// string ourselves, which can be quite complicated, and would
	// unnecessarily duplicate code
	// already implemented by the format's parse() method.
	// Convert the parsed value to a simple string (decimal point only)
	// and use it to set the monetary value.
	value = new BigDecimal(Double.toString(amount)).setScale(2, BigDecimal.ROUND_HALF_UP);
    } // Constructor Money(double amount)

    /**
     * 
     * Constructs a Money object from a long integer value. The specified value
     * represents whole dollars only (that is, cents are implicitly assumed to
     * be 00). For example, the integer 25 would represent a monetary value of
     * $25.00.
     * 
     * <p>
     * 
     * @param <b>amount </b> The monetary amount, in whole dollars (no cents)
     *        </p>
     * 
     */
    public Money(long amount) // Monetary amount, whole dollars (no cents)
    {
	value = new BigDecimal(Long.toString(amount)).setScale(2); // Set
								   // monetary
								   // value.
    }

    /**
     * 
     * Constructs a Money object from a long integer value with a specified
     * scale factor. The scale factor (0, 1, or 2) specifies the number of
     * digits to the right of an implied decimal point.
     * 
     * <p>
     * For example, the value 1995 would be interpreted as a monetary value of
     * $1995.00, $199.50, and $19.95 for scale factors of 0, 1, or 2,
     * respectively.
     * </p>
     * 
     * <p>
     * 
     * @param <b>amount </b> The monetary amount, in dollars and cents
     * 
     * @param <b>scale </b> Scale factor (must be 0, 1, or 2)
     *        </p>
     * 
     *        <p>
     * @throws InvalidScaleFactorException
     *             The scale factor specified is not valid (must be 0, 1, or 2)
     *             </p>
     * 
     */
    public Money(long amount, // Monetary amount
	    int scale) // Scale Factor (0, 1, 2)
	    throws InvalidScaleFactorException {
	if ( // If the Scale Factor is not
	(scale < 0) // 0, 1, or
		|| (scale > 2) // 2,
	) {
	    throw new InvalidScaleFactorException("Invalid scale factor: " + scale + " (must be 0, 1, or 2)");
	}
	// Set the monetary value and scale as specified.
	value = (new BigDecimal(Long.toString(amount))).movePointLeft(scale).setScale(2, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 
     * Constructs a Money object from a string representation of a monetary
     * value. The format of the string must be consistent with the Currency
     * Format; otherwise, a ParseException is recognized.
     * 
     * Refer to the Java API documentation for the DecimalFormat class for
     * information on Decimal Formats in general, and Currency Formats in
     * particular.
     * 
     * <p>
     * 
     * @param <b>string </b> A String representing a monetary value
     *        </p>
     * 
     *        <p>
     * @throws ParseException
     *             The string is inconsistent with the Currency Format
     *             </p>
     * 
     * @see DecimalFormat
     * @see #setCurrencyFormat
     * 
     */
    public Money(String string) {
	try {
	    if (StringUtils.isBlank(string)) {
		value = ZERO.value;
		return;
	    }

	    value = ConversionUtils.toBigDecimal(string);
	    if (value == null) {
		Number number = currencyFormat.parse(string);
		value = new BigDecimal(number.toString());
	    }
	    value = value.setScale(2, BigDecimal.ROUND_HALF_UP);
	} catch (ParseException x) {
	    throw new NumberFormatException("Not a valid value:" + string);
	}
    }

    /**
     * 
     * Constructs a Money object from a BigDecimal object.
     * 
     * <p>
     * 
     * @param <b>amount </b> A BigDecimal value representing a monetary amount,
     *        in dollars and cents
     *        </p>
     * 
     */
    public Money(BigDecimal amount) {
	value = new BigDecimal(amount.toString()).setScale(2, BigDecimal.ROUND_HALF_UP); // Set
											 // the
											 // monetary
											 // value.
    } // Constructor Money(BigDecimal amount)

    /**
     * 
     * Copy Constructor; constructs a Money object from another Money object.
     * 
     * <p>
     * 
     * @param <b>amount </b> A Money object
     *        </p>
     * 
     */
    public Money(Money amount) {
	roundingMode = amount.roundingMode; // Copy the Rounding Mode.
	// Clone the Currency Format.
	currencyFormat = (DecimalFormat) amount.currencyFormat.clone();
	value = amount.value.setScale(2, BigDecimal.ROUND_HALF_UP); // Copy the
								    // Monetary
								    // Value.
    }

    /**
     * 
     * Adds a specified monetary value to this monetary value.
     * 
     * <p>
     * The value of this object is not affected in any way. A new object is
     * returned reflecting the result of the operation.
     * </p>
     * 
     * <p>
     * 
     * @param <b>money </b> The monetary value to be added
     *        </p>
     * 
     *        <p>
     * @return A new Money object representing the result
     *         </p>
     * 
     */
    public Money add(Money money) {
	if (money == null) {
	    return this;
	}
	Money result = new Money(this); // Create a new Money object for the
					// result.
	result.value = value.add(money.getValue()); // Add the two monetary
						    // values.
	return result;
    }

    public Money add(double dvalue) {
	return add(new Money(dvalue));
    }

    public Money add(BigDecimal decimal) {
	return add(new Money(decimal));
    }

    /**
     * 
     * Subtracts a specified monetary value from this monetary value.
     * 
     * <p>
     * The value of this object is not affected in any way. A new object is
     * returned reflecting the result of the operation.
     * </p>
     * 
     * <p>
     * 
     * @param <b>money </b> The monetary value to be subtracted
     *        </p>
     * 
     *        <p>
     * @return A new Money object representing the result
     *         </p>
     * 
     */
    public Money subtract(Money money) {
	Money result = new Money(this); // Create a new Money object for the
					// result.
	result.value = value.subtract(money.getValue()); // Subtract the
							 // specified monetary
							 // value.
	return result;
    }

    public Money subtract(double dvalue) {
	return subtract(new Money(dvalue));
    }

    public Money subtract(BigDecimal dvalue) {
	return subtract(new Money(dvalue));
    }

    /**
     * 
     * Multiplies this monetary value by a specified value.
     * 
     * <p>
     * The value of this object is not affected in any way. A new object is
     * returned reflecting the result of the operation.
     * </p>
     * 
     * <p>
     * 
     * @param <b>mult </b> The multiplier value
     *        </p>
     * 
     *        <p>
     * @return A new Money object representing the result
     *         </p>
     * 
     */
    public Money multiply(double mult) {
	Money result = new Money(this); // Create a new Money object for the
					// result.
	// Multiply by the specified value.
	result.value = value.multiply(new BigDecimal(Double.toString(mult)));
	return result;
    }

    public Money multiply(Money mult) {
	Money result = new Money(this); // Create a new Money object for the
					// result.
	// Multiply by the specified value.
	result.value = value.multiply(mult.getValue());
	return result;
    }

    /**
     * 
     * Multiplies this monetary value by a specified value.
     * 
     * <p>
     * The value of this object is not affected in any way. A new object is
     * returned reflecting the result of the operation.
     * </p>
     * 
     * <p>
     * 
     * @param <b>mult </b> The multiplier value
     *        </p>
     * 
     *        <p>
     * @return A new Money object representing the result
     *         </p>
     * 
     */
    public Money multiply(long mult) {
	Money result = new Money(this); // Create a new Money object for the
					// result.
	// Multiply by the specified value.
	result.value = value.multiply(new BigDecimal(Long.toString(mult)));
	return result;
    }

    public Money multiply(BigDecimal mutl) {
	Money result = new Money(this);
	result.value = value.multiply(mutl);
	return result;
    }

    /**
     * 
     * Divides this monetary value by a specified value.
     * 
     * <p>
     * The value of this object is not affected in any way. A new object is
     * returned reflecting the result of the operation.
     * </p>
     * 
     * <p>
     * 
     * @param <b>div </b> The divisor value
     *        </p>
     * 
     *        <p>
     * @return A new Money object representing the result
     *         </p>
     * 
     */
    public Money divide(double div) {
	Money result = new Money(this); // Create a new Money object for the
					// result.
	// Divide the monetary value by the specified value and round if
	// necessary.
	result.value = value.divide(new BigDecimal(Double.toString(div)), BigDecimal.ROUND_HALF_UP);
	return result;
    }

    /**
     * 
     * Divides this monetary value by a specified value.
     * 
     * <p>
     * The value of this object is not affected in any way. A new object is
     * returned reflecting the result of the operation.
     * </p>
     * 
     * <p>
     * 
     * @param <b>div </b> The divisor value
     *        </p>
     * 
     *        <p>
     * @return A new Money object representing the result
     *         </p>
     * 
     */
    public Money divide(long div) {
	Money result = new Money(this); // Create a new Money object for the
					// result.
	// Divide the monetary value by the specified value and round if
	// necessary.
	result.value = value.divide(new BigDecimal(Long.toString(div)), BigDecimal.ROUND_HALF_UP);
	return result;
    }

    /**
     * 
     * Divides this monetary value by a specified value.
     * 
     * <p>
     * The value of this object is not affected in any way. A new object is
     * returned reflecting the result of the operation.
     * </p>
     * 
     * <p>
     * 
     * @param <b>div </b> The divisor value
     *        </p>
     * 
     *        <p>
     * @return A new Money object representing the result
     *         </p>
     * 
     */
    public Money divide(Money div) {
	Money result = new Money(this); // Create a new Money object for the
					// result.
	// Divide the monetary value by the specified value and round if
	// necessary.
	result.value = value.divide(div.getValue(), BigDecimal.ROUND_HALF_UP);
	return result;
    }

    public Money divide(Money div, int scale) {
	Money result = new Money(this); // Create a new Money object for the
					// result.
	// Divide the monetary value by the specified value and round if
	// necessary.
	result.value = value.divide(div.getValue(), scale, BigDecimal.ROUND_HALF_UP);
	return result;
    }

    /**
     * 
     * Negates this monetary value.
     * 
     * Positive values become negative, and negative values become positive. The
     * effect is the same as if the monetary value were multiplied by -1.
     * 
     * <p>
     * The value of this object is not affected in any way. A new object is
     * returned reflecting the result of the operation.
     * </p>
     * 
     * <p>
     * 
     * @return A new Money object representing the result
     *         </p>
     * 
     */
    public Money negate() {
	Money result = new Money(this); // Create a new Money object for the
					// result.
	result.value = value.negate(); // Negate the monetary value.
	return result;
    }

    /**
     * 
     * Returns the absolute monetary value.
     * 
     * A positive value is returned, irrespective of whether the monetary value
     * is positive or negative.
     * 
     * <p>
     * The value of this object is not affected in any way. A new object is
     * returned reflecting the result of the operation.
     * </p>
     * 
     * <p>
     * 
     * @return A new Money object representing the result
     *         </p>
     * 
     */
    public Money abs() {
	Money result = new Money(this); // Create a new Money object for the
					// result.
	result.value = value.abs(); // Get the absolute monetary value.
	return result;
    }

    /**
     * 
     * Returns the monetary value as a long integer with 2 decimal digits to the
     * right of an implicit decimal point. For example, if the monetary value
     * were $19.95, a value of 1995 would be returned. Note that the monetary
     * value is rounded, if necessary, according to the specified Rounding Mode.
     * 
     * <p>
     * 
     * @return The monetary value
     *         </p>
     * 
     */
    public long toLong() {
	// Round off the monetary value to 2 decimal places.
	BigDecimal result = value.setScale(2, roundingMode);
	result = result.movePointRight(2); // Move decimal point 2 places to the
					   // right to preserve cents.
	return result.longValue();
    }

    /**
     * 
     * Returns the monetary value as a double-precision, floating-point value.
     * 
     * <p>
     * Note: Exercise care when converting monetary values to floating point
     * values, because floating-point arithmetic is not well-suited for use with
     * monetary data.
     * </p>
     * 
     * <p>
     * 
     * @return The monetary value
     *         </p>
     * 
     */
    public double toDouble() {
	// Return the monetary value as a double floating point value.
	return value.setScale(2, roundingMode).doubleValue();
    }

    /**
     * 
     * Returns the monetary value as a BigDecimal object.
     * 
     * <p>
     * 
     * @return The monetary value
     *         </p>
     * 
     */
    public BigDecimal getValue() {
	// Return the monetary value.
	return value.setScale(2, roundingMode);
    }

    /**
     * 
     * Returns the Rounding Mode.
     * 
     * Refer to Java's BigDecimal object for a description of the possible
     * rounding modes.
     * 
     * <p>
     * 
     * @return The Rounding Mode
     *         </p>
     * 
     * @see BigDecimal#ROUND_UP
     * @see BigDecimal#ROUND_DOWN
     * @see BigDecimal#ROUND_CEILING
     * @see BigDecimal#ROUND_FLOOR
     * @see BigDecimal#ROUND_HALF_UP
     * @see BigDecimal#ROUND_HALF_DOWN
     * @see BigDecimal#ROUND_HALF_EVEN
     * 
     */
    public int getRoundingMode() {
	// Return the Rounding Mode.
	return roundingMode;
    }

    /**
     * 
     * Sets the Rounding Mode.
     * 
     * Refer to the Java API documentation for the BigDecimal class for a
     * description of the possible Rounding Modes.
     * 
     * The default Rounding Mode is BigDecimal.ROUND_DOWN, which effectively
     * discards any fractional cent amount and truncates the monetary value to 2
     * decimal places.
     * 
     * A Rounding Mode of BigDecimal.ROUND_UNNECESSARY is not valid for use with
     * monetary data since certain operations result in a loss of precision and
     * therefore require that a rounding mode be explicitly specified.
     * 
     * <p>
     * The value of this object is not affected in any way. A new object is
     * returned reflecting the result of the operation.
     * </p>
     * 
     * <p>
     * 
     * @param <b>int </b> The Rounding Mode
     *        </p>
     * 
     *        <p>
     * @return A new Money object representing the result
     *         </p>
     * 
     *         <p>
     * @throws InvalidRoundingModeException
     *             The Rounding Mode specified is not valid for monetary data
     *             </p>
     * 
     * @see BigDecimal#ROUND_UP
     * @see BigDecimal#ROUND_DOWN
     * @see BigDecimal#ROUND_CEILING
     * @see BigDecimal#ROUND_FLOOR
     * @see BigDecimal#ROUND_HALF_UP
     * @see BigDecimal#ROUND_HALF_DOWN
     * @see BigDecimal#ROUND_HALF_EVEN
     * 
     */
    public Money setRoundingMode(int mode) throws InvalidRoundingModeException {
	if (mode == BigDecimal.ROUND_UNNECESSARY) {
	    throw new InvalidRoundingModeException("Rounding mode not valid for monetary data: " + mode);
	}
	Money result = new Money(this); // Create a new Money object for the
					// result.
	result.roundingMode = mode; // Set the Rounding Mode.
	return result;
    }

    /**
     * 
     * Returns the Currency Format, used to format and parse monetary values.
     * 
     * Refer to the Java API documentation for the DecimalFormat class for
     * information on Decimal Formats in general, and Currency Formats in
     * particular.
     * 
     * <p>
     * 
     * @return The Currency Format
     *         </p>
     * 
     * @see DecimalFormat
     * 
     */
    public DecimalFormat getCurrencyFormat() {
	// Return the Currency Format.
	return currencyFormat;
    }

    /**
     * 
     * Sets the Currency Format, used for formatting and parsing monetary
     * values.
     * 
     * Refer to the Java API documentation for the DecimalFormat class for
     * information on Decimal Formats in general, and Currency Formats in
     * particular.
     * 
     * <p>
     * By default, the Currency Format for the current locale is used. For the
     * United States, the default Currency Symbol is the Dollar Sign ("$").
     * Negative amounts are enclosed in parentheses. A Decimal Point (".")
     * separates the dollars and cents, and a comma (",") separates each group
     * of 3 consecutive digits in the dollar amount.
     * </p>
     * 
     * <p>
     * The value of this object is not affected in any way. A new object is
     * returned reflecting the result of the operation.
     * </p>
     * 
     * <p>
     * 
     * @param <b>format </b> The Currency Format
     *        </p>
     * 
     *        <p>
     * @return A new Money object representing the result
     *         </p>
     * 
     * @see DecimalFormat
     * 
     */
    public Money setCurrencyFormat(DecimalFormat format) {
	Money result = new Money(this); // Create a new Money object for the
					// result.
	result.currencyFormat = format; // Set the Currency Format.
	return result;
    }

    /**
     * Returns an unformatted representation of the money value.
     * 
     * This is different than the Money class as originally published in Dr.
     * Dobb's. I see no reason to format the text, in fact, I think formatting
     * the text will introduce bugs because developers will probably expect the
     * Money class to work in a way similar to BigDecimal when it comes to the
     * toString method.
     */
    @Override
    public String toString() {
	// Round off the monetary value to 2 decimal places.
	return value.setScale(2, roundingMode).toString();
    }

    /**
     * 
     * Parses a string representation of a monetary value, returning a new Money
     * object with the specified value. The format of the string must be
     * consistent with the Currency Format; otherwise, a ParseException is
     * recognized.
     * 
     * Refer to the Java API documentation for the DecimalFormat class for
     * information on Decimal Formats in general, and Currency Formats in
     * particular.
     * 
     * <p>
     * The value of this object is not affected in any way. A new object is
     * returned reflecting the result of the operation.
     * </p>
     * 
     * <p>
     * 
     * @param <b>string </b> A String representing a monetary value
     *        </p>
     * 
     *        <p>
     * @return A new Money object representing the parsed monetary value
     *         </p>
     * 
     *         <p>
     * @throws ParseException
     *             The string is inconsistent with the Currency Format
     *             </p>
     * 
     * @see DecimalFormat
     * @see #setCurrencyFormat
     * 
     */
    public Money parse(String string) throws ParseException // If string is
							    // inconsistent with
    // Currency Format
    {
	Money result = new Money(this); // Create a Money object for the
	// result.
	// We make use of the format's parse() method, which parses the string
	// according to the format and returns either a Double or a Long
	// object representing the value.
	Number number;
	// Attempt to parse the string as a monetary value. May throw a
	// ParseException if the string is not consistent with the Currency
	// Format.
	number = currencyFormat.parse(string);
	// In general, a double floating point value cannot represent a
	// decimal value exactly, and
	// therefore is only a very close approximation of the actual decimal
	// value. Fortunately,
	// the Double.toString() method is able to "recognize" an approximated
	// decimal value, and
	// will return the original (approximated) decimal value, rather than
	// the literal floating
	// point value. Here, we take advantage of this fact to obtain a
	// string representation of
	// the monetary value (without formatting, except for the decimal
	// point), which we then use
	// to create a BigDecimal object having the required value.
	// Were we not to make this simplifying assumption, the only
	// alternative would be to parse the
	// string ourselves, which can be quite complicated, and would
	// unnecessarily duplicate code
	// already implemented by the format's parse() method.
	// Convert the parsed value to a simple string (decimal point only)
	// and use it to set the monetary value.
	result.value = new BigDecimal(number.toString());
	return result; // Return the new Money object.
    }

    /**
     * 
     * Returns an indication of whether or not this monetary value is zero
     * (equal to $0.00).
     * 
     * <p>
     * 
     * @return <b>true </b> The monetary value is zero <br>
     *         <b>false</b> The monetary value is not zero
     *         </p>
     * 
     */
    public boolean isZero() {
	// Return true if value is zero.
	return (value.compareTo(BIGDECIMAL_ZERO) == 0);
    }

    public boolean isNotZero() {
	// Return true if value is not zero.
	return (value.compareTo(BIGDECIMAL_ZERO) != 0);
    }

    /**
     * 
     * Returns an indication of whether or not this monetary value is negative
     * (less than $0.00).
     * 
     * <p>
     * 
     * @return <b>true </b> The monetary value is negative <br>
     *         <b>false</b> The monetary value is not negative
     *         </p>
     * 
     */
    public boolean isNegative() {
	// Return true if value is less than zero.
	return (value.compareTo(BIGDECIMAL_ZERO) < 0);
    }

    /**
     * 
     * Returns an indication of whether or not this monetary value is positive
     * (greater than or equal to $0.00).
     * 
     * <p>
     * 
     * <b>false</b> The monetary value is not positive
     * 
     * @return <b>true </b> The monetary value is positive <br>
     *         </p>
     * 
     */
    public boolean isPositive() {
	// Return true if value is greater than zero.
	return (value.compareTo(BIGDECIMAL_ZERO) >= 0);
    }

    /**
     * 
     * Returns an indication of whether or not this monetary value is equal to
     * another monetary value.
     * 
     * <p>
     * 
     * @param <b>other </b> A monetary value with which this monetary value is
     *        to be compared
     *        </p>
     * 
     *        <p>
     * @return <b>true </b> This monetary values are equal <br>
     *         <b>false</b> This monetary values are not equal
     *         </p>
     * 
     */
    public boolean isEqual(Money other) {
	// Return true if equal to the other amount.
	return (value.compareTo(other.value) == 0);
    }

    public boolean isNotEqual(Money other) {
	// Return true if not equal to the other amount.
	return (value.compareTo(other.value) != 0);
    }

    /**
     * 
     * Returns an indication of whether or not this monetary value is less than
     * another monetary value.
     * 
     * <p>
     * 
     * @param <b>other </b> A monetary value with which this monetary value is
     *        to be compared
     *        </p>
     * 
     *        <p>
     * @return <b>true </b> This monetary value is less than the specified
     *         monetary value <br>
     *         <b>false</b> This monetary value is not less than the specified
     *         monetary value
     *         </p>
     * 
     */
    public boolean isLessThan(Money other) {
	// Return true if less than the other amount.
	return (value.compareTo(other.value) < 0);
    }

    /**
     * 
     * Returns an indication of whether or not this monetary value is less than
     * or equal to another monetary value.
     * 
     * <p>
     * 
     * @param <b>other </b> A monetary value with which this monetary value is
     *        to be compared
     *        </p>
     * 
     *        <p>
     * @return <b>true </b> This monetary value is less than or equal to the
     *         specified monetary value <br>
     *         <b>false </b> This monetary value is not less than or equal to
     *         the specified monetary value
     *         </p>
     * 
     */
    public boolean isLessThanOrEqual(Money other) {
	// Return true if less than or equal to the other amount.
	return (value.compareTo(other.value) <= 0);
    }

    public boolean isGreaterThanZero() {
	return (value.compareTo(BIGDECIMAL_ZERO) > 0);
    }

    public boolean isLessThanZero() {
	return (value.compareTo(BIGDECIMAL_ZERO) < 0);
    }

    /**
     * 
     * Returns an indication of whether or not this monetary value is greater
     * than another monetary value.
     * 
     * <p>
     * 
     * @param <b>other </b> A monetary value with which this monetary value is
     *        to be compared
     *        </p>
     * 
     *        <p>
     * @return <b>true </b> This monetary value is greater than the specified
     *         monetary value <br>
     *         <b>false </b> This monetary value is not greater than the
     *         specified monetary value
     *         </p>
     * 
     */
    public boolean isGreaterThan(Money other) {
	// Return true if greater than the other amount.
	return (value.compareTo(other.value) > 0);
    }

    /**
     * 
     * Returns an indication of whether or not this monetary value is greater
     * than or equal to another monetary value.
     * 
     * <p>
     * 
     * @param <b>other </b> A monetary value with which this monetary value is
     *        to be compared
     *        </p>
     * 
     *        <p>
     * @return <b>true </b> This monetary value is greater than or equal to the
     *         specified monetary value <br>
     *         <b>false </b> This monetary value is not greater than or equal to
     *         the specified monetary value
     *         </p>
     * 
     */
    public boolean isGreaterThanOrEqual(Money other) {
	// Return true if greater than or equal to the other amount.
	return (value.compareTo(other.value) >= 0);
    }

    /**
     * 
     * Compares this object with the specified object. The objects are equal if
     * and only if the specified object is not null, is a Money object, and has
     * the same monetary value as this object.
     * 
     * <p>
     * 
     * @param <b>object </b> Some object
     *        </p>
     * 
     *        <p>
     * @return <b>true </b> The objects are equal <br>
     *         <b>false </b> The objects are not equal
     *         </p>
     * 
     */
    @Override
    public boolean equals(Object object) {
	if (object == this) {
	    return true;
	}
	if (object == null) {
	    return false;
	}
	if (!(object instanceof Money)) {
	    return false;
	}
	return (value.compareTo(((Money) object).value) == 0);
    }

    /**
     * 
     * Returns a hash code for this object. The hash code is identical to that
     * for the BigDecimal object that represents the monetary value.
     * 
     * <p>
     * 
     * @return The hash code
     *         </p>
     * 
     */
    @Override
    public int hashCode() {
	return value.hashCode();
	// Return the hash code for the monetary value.
    }

    public boolean isZeroOrLess() {
	return isZero() || isNegative();
    }

    /**
     * 
     * Clones a Money object. The new object is an exact copy of this object,
     * and inherits the object's monetary value and Currency Format.
     * 
     * <p>
     * 
     * @return <b>Money </b> The cloned object
     *         </p>
     * 
     */
    @Override
    public Object clone() {
	return new Money(this); // Return the cloned object.
    }

    public Money add(String amount) {
	return add(new Money(amount));
    }

    @Override
    public double doubleValue() {
	return getValue().doubleValue();
    }

    @Override
    public float floatValue() {
	return getValue().floatValue();
    }

    @Override
    public int intValue() {
	return getValue().intValue();
    }

    @Override
    public long longValue() {
	return getValue().longValue();
    }

    public Money subtract(String value1) {
	return subtract(new Money(value1));
    }

    @Override
    public int compareTo(Money o) {
	return value.compareTo(o.value);
    }

    public int compareTo(BigDecimal o) {
	return value.compareTo(o);
    }
}
